package service

import (
	"dbserver/config"
	"dbserver/database"
	"fmt"
	"net"
	"net/tcpserver"
	"share/app"
	"share/msg"
	"sync"
	l "util/log"
	"util/parser"
)

var (
	conn    *database.MySql
	db      string
	path    string
	dbs     *dbserver
	appbook *app.AppClient
)

type client struct {
	address net.Addr
}

type dbserver struct {
	sock    *tcpserver.TcpServer
	clients map[int]*client
	lock    sync.Mutex
}

func (s *dbserver) OnCreate(ts *tcpserver.TcpServer) {
	s.sock = ts
	if err := s.sock.Start(1); err != nil {
		l.LogError(err)
	}
}

func (s *dbserver) OnConnect(index int, serial int, addr net.Addr) {
	s.lock.Lock()
	defer s.lock.Unlock()
	c := &client{}
	c.address = addr
	if _, dup := s.clients[index]; dup {
		delete(s.clients, index)
	}

	s.clients[index] = c
	l.TraceInfo("dbserver", "client connected: ", addr.String())
}

func (s *dbserver) OnDisconnect(index int, serial int) {
	s.lock.Lock()
	defer s.lock.Unlock()

	if c, ok := s.clients[index]; ok {
		l.TraceInfo("dbserver", "client closed: ", c.address.String())
		delete(s.clients, index)
	}

}

func (s *dbserver) Recv(index int, serial int, data []byte, size int) {
	go parseMsg(index, serial, data, size)
}

func CreateDbService(cfg *config.Config, drop bool, sync bool) bool {

	c, err := database.NewConn("mysql", cfg.Datasource)
	if err != nil {
		l.TraceInfo("dbmgr", err)
		return false
	}

	var ok bool
	if conn, ok = c.(*database.MySql); !ok {
		return false
	}

	db = cfg.Database
	path = cfg.DefPath

	if drop {
		conn.DropDB(db)
		l.TraceInfo("dbmgr", "drop db ", db)
		return false
	}

	conn.Exec(fmt.Sprintf("CREATE DATABASE IF NOT EXISTS `%s`", db))

	conn.UseDB(db)

	l.TraceInfo("dbmgr", "use ", db)
	if sync {
		syncDB(path)
		return false
	}

	if !checkDb(path) {
		return false
	}

	parser.LoadAllDef(path)

	dbs = &dbserver{}
	tcpserver.CreateTcpServer(dbs, cfg.DbIp, int(cfg.DbPort), int(cfg.MaxClient))

	l.TraceInfo("dbserver", "start service at ", cfg.DbPort)

	appbook = app.CreateAppClient()

	appbook.Run(cfg.CenterIp, int(cfg.CenterPort), msg.AT_DB, cfg.DbIp, int(cfg.DbPort))

	return true
}

func StopDBService() {
	l.TraceInfo("dbserver", "stop service")

	if appbook != nil {
		appbook.Shutdown()
	}

	if dbs.sock != nil {
		dbs.sock.Close(true)
		dbs.sock = nil
		dbs = nil
	}

	conn.Close()
	conn = nil

}

func Status(s string) string {

	rows, err := conn.Query(fmt.Sprintf("show global status like '%s'", s))
	if err != nil {
		return err.Error()
	}
	defer rows.Close()
	ig := ""
	t := 0
	if rows.Next() {
		rows.Scan(&ig, &t)
	}

	return fmt.Sprintf("%s:%d", s, t)

}
